# coding=utf-8
import os
from string import Template

from typing import List
from parse.sdb_parser import ECU
from utils.templete_file import TempleteFile
from utils.util import writeFile


templete_h = Template(
    """
#pragma once

#include <vector>
#include <transfer/common.h>
#include "common.h"

namespace ecarx::transfer::utils{
    
    std::vector<Signal> ipcpToSignals(const ipcp::IpcpValue&);

}
"""
)
templete_cpp = Template(
    """
#include "convertFromIPCPValue.h"
#include "vp/SignalServiceId.h"
#include "ap/signal_id.h"
#include "vp/RX2.h"
#include <arpa/inet.h>
#include <string.h>

${ecu_h}

namespace ecarx::transfer::utils{
    
    std::vector<Signal> ipcpToSignals(const ipcp::IpcpValue& ipcpValue){

        std::vector<Signal> signals={};
        std::vector<ipcp::IpcpValue> ipcpValues;
        if ((ipcpValue.service_id == EcarXSignalServiceID::serviceID_RX1 && ipcpValue.oper_id == RX1NotificationOpID) || 
            (ipcpValue.service_id == EcarXSignalServiceID::serviceID_RX2 && ipcpValue.oper_id == RX2NotificationOpID)){
            RXHeader  header;
            int len = 0;
            uint16_t vpNumber = 0;
            while( len < ipcpValue.payload_len ){
                memcpy(&header,(uint8_t *)ipcpValue.payload + (uintptr_t)len, sizeof(RXHeader));
                len += sizeof(RXHeader);
                vpNumber =ntohs(header.ApVpAuthentication);
                uint16_t serviceId = ntohs(header.serviceid);
                uint16_t headerLen = ntohs(header.len);
                switch(serviceId){${splite_package}
                }
                len += headerLen;
            }
        }else{
            ipcpValues.push_back(ipcpValue);
        }
        for(auto& servicePackage : ipcpValues){
            switch(servicePackage.service_id){${codes}
            }
        }
        return signals;
    }
}
"""
)
case_tempelet = Template(
"""
                case ${id}:{${codes}
                }
                break;""")

class Case(TempleteFile):

    def __init__(self, id) -> None:
        self.id = id
        self.code = []
        self.case_ident=0
        self.block_ident=1

    
    def setCaseIndent(self,num):
        self.case_indent = num

    def setBlockIdent(self,num):
        self.block_ident = num

    def codeBlock(self, code: str):
        self.code = code
        return self

    def toStr(self) -> str:
        return case_tempelet.substitute(id=self.id, codes=self.code)


def generateSignalFromIPCP2Value(ecus:List[ECU],outDir:str):
    ecu_headers = []
    cases:List[Case]=[]
    split_cases:List[Case]=[]
    for ecu in ecus:
        if ecu.hasReadSignals():
            ecu_headers.append("#include \"vp/{0}_RX.h\"".format(ecu.name.upper()))
            case = Case(
                "EcarXSignalServiceID::{}".format(ecu.getServiceId()[0]))
            templete = Template("""
                    constexpr int structLen=(int)sizeof(Op${ecu}_Notification);
                    int bufferLength=structLen;
                    if (servicePackage.payload_len < structLen  ) {
                        bufferLength = servicePackage.payload_len;
                    }
                    Op${ecu}_Notification pa;
                    memcpy(&pa, servicePackage.payload, bufferLength);
                    signals.reserve(${len});${signals}
""")
            signal_templete = Template("""
                    signals.emplace_back(Signal{
                        .signalId=${signalId},
                        .value.${prop} = { ${value} }
                    });""")
            split_case=Case(
                    "EcarXSignalServiceID::{}".format(ecu.getServiceId()[0]))
            split_templete = Template("""
                        ipcp::IpcpValue dest;
                        dest.service_id = serviceId;
                        dest.oper_id = ${op_id};
                        dest.payload_len = sizeof(Op${ecu}_Notification);
                        dest.payload = (void*)new uint8_t[dest.payload_len];
                        memcpy(dest.payload,(uint8_t *)ipcpValue.payload + (uintptr_t)len, sizeof(Op${ecu}_Notification));
                        ipcpValues.push_back(dest);""")
            split_case.codeBlock(split_templete.substitute(
                op_id = "{0}NotificationOpID".format(ecu.name.upper()),
                ecu = ecu.name.upper()
            ))
            split_cases.append(split_case)
            # 逐个解析所有的信号
            parse = []
            for rx in ecu.rxPackage.signals:
                (name,_) = rx.getSignalIdTypeDefine()
                signal = signal_templete.substitute(
                    signalId="(int32_t)SignalRX::"+rx.name,
                    prop="int32Values" if name == "INT32" else "floatValues",
                    value=rx.readSignalFromNet("pa."+rx.getVariableName())
                )
                parse.append(signal)
            case.codeBlock(templete.substitute(
                ecu=ecu.name.upper(), len=len(ecu.rxPackage.signals), signals="".join(parse)))
            cases.append(case)
    

    writeFile(os.path.join(outDir,"src"), "convertFromIPCPValue.h",lambda file:file.write(templete_h.substitute()))
    writeFile(os.path.join(outDir,"src"), "convertFromIPCPValue.cpp",lambda file:file.write(templete_cpp.substitute(
            ecu_h="\n".join(ecu_headers),
            codes="".join([case.toStr() for case in cases]),
            splite_package = "\t".expandtabs(25).join([case.toStr() for case in split_cases]))))

